package com.pansoft.openplanet.account.util;

import com.efounder.constant.EnvironmentVariable;
import com.efounder.util.EnvSupportManager;
import com.pansoft.hd.HDWalletGenerator;
import com.pansoft.hd.HDWalletUtil;
import com.pansoft.hd.model.BtcHdWallet;
import com.pansoft.openplanet.account.model.BtcBean;
import com.pansoft.openplanet.bean.Account;
import com.pansoft.openplanet.constant.IPAssetsConstant;
import com.pansoft.openplanet.util.BTCMnemonicUtils;
import com.pansoft.openplanet.util.BtcNetParamUtil;
import com.pansoft.openplanet.util.TCAccountManager;
import com.utilcode.util.LogUtils;
import com.utilcode.util.TimeUtils;

import org.bitcoinj.core.Address;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.LegacyAddress;
import org.bitcoinj.crypto.DeterministicKey;
import org.bitcoinj.wallet.DeterministicSeed;
import org.bitcoinj.wallet.Wallet;
import org.web3j.utils.Numeric;

import java.io.File;
import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;

import static com.efounder.frame.utils.Constants.CHAT_USER_ID;
import static com.pansoft.hd.HDWalletUtil.getDeterministicSeed;
import static com.pansoft.hd.HDWalletUtil.getNetworkParameters;
import static com.pansoft.hd.HDWalletUtil.mnemonicString2List;

/**
 * BTC账户生成
 *
 * @author YQS 2018/06/08
 */
public class BTCAccountGenerator {
    private static final String TAG = "BTCAccountGenerator";


    /**
     * todo 异步方法创建账户
     *
     * @param keyStoreDirPath keystore文件存放目录
     * @return
     * @throws Exception
     */
    public static Disposable createBtcAccountAsync(final String keyStoreDirPath, final GeneratorListener generatorListener) {
        //之前非bip49钱包需要保存到本地
        if (!EnvSupportManager.isSupportHDWallet()) {
            if (keyStoreDirPath == null) {
                if (generatorListener != null) {
                    generatorListener.generatorFail();
                }
                return null;
            }
            //如果目录不存在，创建目录
            File file = new File(keyStoreDirPath);
            if (!file.exists() || !file.isDirectory()) {
                file.mkdirs();
            }
        }

        Disposable disposable = Observable.create(new ObservableOnSubscribe<BtcBean>() {
            @Override
            public void subscribe(ObservableEmitter<BtcBean> emitter) throws Exception {
                BtcBean ethBean = createBtcAccountSync(keyStoreDirPath);
                //加密助记词
                String secretMnemonic = TCAccountManager.encryptByMainPrivKey(ethBean.getMnemonic());
                ethBean.setSecretMnemonic(secretMnemonic);
                emitter.onNext(ethBean);
                emitter.onComplete();
            }
        })        // 在io线程生成walletfile的进行操作
                .subscribeOn(Schedulers.io())
                // 在主线程处理walletfile保存
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<BtcBean>() {
                    @Override
                    public void accept(@NonNull BtcBean ethBean) throws Exception {
                        if (generatorListener != null) {
                            generatorListener.generatorSuccess(ethBean);
                        }

                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        if (generatorListener != null) {
                            generatorListener.generatorFail();
                        }
                    }
                });
        return disposable;
    }

    /**
     * todo 同步方法创建 账户
     *
     * @param keyStoreDirPath keystore 文件存放目录
     * @return
     * @throws Exception
     */
    public static BtcBean createBtcAccountSync(String keyStoreDirPath) throws Exception {

        //生成助记词
        DeterministicSeed deterministicSeed = BTCMnemonicUtils.createRandomDeterministicSeed("");
        List<String> mnemonicList = deterministicSeed.getMnemonicCode();
        String mnemonic = HDWalletUtil.mnemonic2String(mnemonicList);
        BtcBean btcBean;
        if (EnvSupportManager.isSupportHDWallet()) {
            btcBean = createBtcBeanFromHdWallet(mnemonic);
        } else {
            //生成eckey
            ECKey ecKey = BTCMnemonicUtils.generateECKey(deterministicSeed);
            //解析公钥
            String publickey = Numeric.toHexStringNoPrefixZeroPadded(new BigInteger(ecKey.getPubKey()), 66);
            //解析私钥
            String privateKey = ecKey.getPrivateKeyEncoded(BtcNetParamUtil.getBtcNetParams()).toString();
            //解析地址
            // String btcAddress = ecKey.(TestNet3Params.get()).toString();

            Address myAddress = LegacyAddress.fromKey(BtcNetParamUtil.getBtcNetParams(), ecKey);

            String btcAddress = myAddress.toString();

            //保存文件
            Wallet wallet = Wallet.fromSeed(BtcNetParamUtil.getBtcNetParams(), deterministicSeed);
            //System.out.println("wallet 生成的btcAddress:" + wallet.currentReceiveAddress());
            //文件名
            SimpleDateFormat dateFormat = new SimpleDateFormat("'BTC--'yyyy-MM-dd'T'HH-mm-ss.SSS'--'");
            String fileName = dateFormat.format(new Date()) + btcAddress + ".wallet";
            File keyStoreFile = new File(keyStoreDirPath, fileName);

            wallet.saveToFile(keyStoreFile);

            LogUtils.i(privateKey, publickey, "eckey 生成的btcAddress:" + btcAddress,
                    "wallet 生成的btcAddress:" + wallet.currentReceiveAddress());

            btcBean = new BtcBean.Builder()
                    .btcAddress(btcAddress)
                    .btcPrivKey(privateKey)
                    .btcPubKey(publickey)
                    .keyStoreFilePath(keyStoreFile.getAbsolutePath())
                    .mnemonic(mnemonic)
                    .mnemonicList(mnemonicList)
                    .build();
        }

        return btcBean;
    }

    /**
     * todo 使用助记词恢复钱包
     *
     * @param keyStoredirPath   keystore 路径
     * @param mnemoric          助记词（" "分割）
     * @param generatorListener
     * @return
     */
    public static Disposable recoverBtcAccountByMnemoric(final String keyStoredirPath, final String mnemoric,
                                                         final GeneratorListener generatorListener) {
        Disposable disposable = Observable.create(new ObservableOnSubscribe<BtcBean>() {
            @Override
            public void subscribe(ObservableEmitter<BtcBean> emitter) throws Exception {
                //助记词恢复
                BtcBean btcBean;
                if (EnvSupportManager.isSupportHDWallet()) {
                    btcBean = createBtcBeanFromHdWallet(mnemoric);
                } else {
                    //如果目录不存在，创建目录
                    File file = new File(keyStoredirPath);
                    if (!file.exists() || !file.isDirectory()) {
                        file.mkdirs();
                    }
                    btcBean = BTCMnemonicUtils.restoreByMnemonic(mnemoric, "");
                }
                //加密助记词
                String secretMnemonic = TCAccountManager.encryptByMainPrivKey(mnemoric);
                btcBean.setSecretMnemonic(secretMnemonic);
                emitter.onNext(btcBean);
                emitter.onComplete();
            }

        }).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<BtcBean>() {
                    @Override
                    public void accept(BtcBean btcBean) throws Exception {
                        if (!EnvSupportManager.isSupportHDWallet()) {
                            //文件名
                            SimpleDateFormat dateFormat = new SimpleDateFormat("'BTC--'yyyy-MM-dd'T'HH-mm-ss.SSS'--'");
                            String fileName = dateFormat.format(new Date()) + btcBean.getBtcAddress() + ".wallet";
                            File keyStoreFile = new File(keyStoredirPath, fileName);

                            btcBean.getWallet().saveToFile(keyStoreFile);
                            btcBean.setKeyStoreFilePath(keyStoreFile.getAbsolutePath());
                        }
                        generatorListener.generatorSuccess(btcBean);
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        if (generatorListener != null) {
                            generatorListener.generatorFail();
                        }
                    }
                });
        return disposable;
    }

    /**
     * 同步方法恢复btc账户
     * @param keyStoredirPath
     * @param mnemoric
     * @return
     * @throws Exception
     */
    public static Account recoverBtcAccountSync(final String keyStoredirPath, final String mnemoric,
                                                String accountName, String accountAvatar) throws Exception{
        //是否HD bip49的btc钱包
        boolean isHdWallet = EnvSupportManager.isSupportHDWallet();
        BtcBean btcBean;
        Account btcAccount = new Account();
        //加密助记词
        String secretMnemonic = TCAccountManager.encryptByMainPrivKey(mnemoric);
        //判断，生成hd bip49的，或者是之前的btc
        if (isHdWallet) {
            //生成HD bip49的btc钱包
            btcBean = createBtcBeanFromHdWallet(mnemoric);
            btcAccount.setHdBtcXpub(btcBean.getHdBtcXpub());
            btcAccount.setHdBtcMaxChildIndex(btcBean.getHdBtcMaxChildIndex());
            btcAccount.setHdBtcCurrentChildAddress(btcBean.getHdBtcCurrentChildAddress());
        } else {
            //如果目录不存在，创建目录
            File file = new File(keyStoredirPath);
            if (!file.exists() || !file.isDirectory()) {
                file.mkdirs();
            }
            //助记词恢复
            btcBean = BTCMnemonicUtils.restoreByMnemonic(mnemoric, "");

            btcBean.setSecretMnemonic(secretMnemonic);
            //文件名
            SimpleDateFormat dateFormat = new SimpleDateFormat("'BTC--'yyyy-MM-dd'T'HH-mm-ss.SSS'--'");
            String fileName = dateFormat.format(new Date()) + btcBean.getBtcAddress() + ".wallet";
            File keyStoreFile = new File(keyStoredirPath, fileName);

            btcBean.getWallet().saveToFile(keyStoreFile);
            btcBean.setKeyStoreFilePath(keyStoreFile.getAbsolutePath());
        }
        btcAccount.setAddress(btcBean.getBtcAddress());
        btcAccount.setAccountAvatar(accountAvatar);
        btcAccount.setAcccountName(accountName);
        btcAccount.setFileName(isHdWallet ? "" : btcBean.getKeyStoreFilePath());
        btcAccount.setImUserId(EnvironmentVariable.getProperty(CHAT_USER_ID));
        btcAccount.setPublicKey(btcBean.getBtcPubKey());
        btcAccount.setSecretMnemonic(secretMnemonic);
        btcAccount.setSecretPrivateKey(TCAccountManager.encryptByMainPrivKey(btcBean.getBtcPrivKey()));
        btcAccount.setMainAccoutAddress(EnvironmentVariable.getProperty("tc_ethAddress", ""));
        btcAccount.setBlockChainName(IPAssetsConstant.ChainEnum.BTC.chainName);
        btcAccount.setCreateTime(TimeUtils.getNowString());
        btcAccount.setEnable(false);

        return btcAccount;
    }

    /**
     * todo 从本地文件加载钱包
     *
     * @param filePath          文件路径
     * @param generatorListener
     * @return
     */
    public static Disposable loadWalletFromFile(final String filePath,
                                                final GeneratorListener generatorListener) {
        Disposable disposable = Observable.create(new ObservableOnSubscribe<BtcBean>() {
            @Override
            public void subscribe(ObservableEmitter<BtcBean> emitter) throws Exception {

                Wallet wallet = Wallet.loadFromFile(new File(filePath));
                DeterministicKey deterministicKey = wallet.currentReceiveKey();
                BigInteger privKeyBTC = deterministicKey.getPrivKey();
                ECKey ecKey = ECKey.fromPrivate(privKeyBTC);
                String publickey = Numeric.toHexStringNoPrefixZeroPadded(new BigInteger(ecKey.getPubKey()), 66);
                //私钥
                String privateKey = ecKey.getPrivateKeyEncoded(BtcNetParamUtil.getBtcNetParams()).toString();
                //地址
                String btcAddress = wallet.currentReceiveAddress().toString();

                LogUtils.i(publickey, privateKey, btcAddress, wallet.toString());

                BtcBean btcBean = new BtcBean.Builder()
                        .btcAddress(btcAddress)
                        .btcPrivKey(privateKey)
                        .btcPubKey(publickey)
                        .wallet(wallet)
                        .build();
                emitter.onNext(btcBean);
                emitter.onComplete();
            }

        }).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<BtcBean>() {
                    @Override
                    public void accept(BtcBean btcBean) throws Exception {
                        if (generatorListener != null) {
                            generatorListener.generatorSuccess(btcBean);
                        }
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        if (generatorListener != null) {
                            generatorListener.generatorFail();
                        }
                    }
                });
        return disposable;
    }

    public static BtcBean createBtcBeanFromHdWallet(String mnemonic) {
        //生成HD bip49的btc钱包
        DeterministicSeed deterministicSeed = getDeterministicSeed(mnemonicString2List(mnemonic),
                "");
        BtcHdWallet btcHdWallet = (BtcHdWallet) HDWalletGenerator
                .createHdBtcWallet(deterministicSeed, BtcNetParamUtil.getBtcBipPath(), getNetworkParameters());
        BtcBean btcBean = new BtcBean.Builder()
                .btcAddress(btcHdWallet.getAddress())
                .btcPrivKey(btcHdWallet.getPrivateKey())
                .mnemonic(mnemonic)
                .btcPubKey(btcHdWallet.getPublicKey())
                .build();
        btcBean.setHdWallet(true);
        btcBean.setHdBtcXpub(btcHdWallet.getXpub());
        //默认只有第一个子地址，path"/0/1", index "1"
        btcBean.setHdBtcCurrentChildAddress(btcHdWallet.getBtcChildAddresses().get(0).getAddress());
        btcBean.setHdBtcMaxChildIndex("1");
        return btcBean;
    }


    /**
     * 钱包创建恢复的回调
     */
    public interface GeneratorListener {
        //成功
        void generatorSuccess(BtcBean ethBean);

        //失败
        void generatorFail();
    }
}
